package com.yunhe.abnormal.listener;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yunhe.abnormal.client.AuthorityClient;
import com.yunhe.abnormal.client.DeviceClient;
import com.yunhe.abnormal.client.MeasHistoryClient;
import com.yunhe.abnormal.client.MeasRealtimeClient;
import com.yunhe.common.constant.*;
import com.yunhe.abnormal.domain.base.AlarmCalculation;
import com.yunhe.abnormal.domain.data.AlarmEvent;
import com.yunhe.abnormal.dto.AlarmNotifyDTO;
import com.yunhe.common.exception.ObjectNotFoundException;
import com.yunhe.common.model.authority.StationDTO;
import com.yunhe.common.model.base.AbnormalStatusDTO;
import com.yunhe.common.model.base.DataTransferDTO;
import com.yunhe.common.model.data.Measurement;
import com.yunhe.common.model.device.Analog;
import com.yunhe.abnormal.service.base.AbnormalStatusService;
import com.yunhe.abnormal.service.base.AlarmRuleInstanceService;
import com.yunhe.abnormal.service.data.AlarmEventService;
import com.yunhe.common.util.DateTimeUtils;
import com.yunhe.common.util.JsonUtil;
import com.yunhe.common.util.RedisClient;
import com.yunhe.common.util.StringUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Component;
import zju.fes.exp.Calculator;
import zju.fes.exp.IllegalExpressionException;
import zju.fes.exp.IncalculableExpressionException;
import zju.fes.exp.UnknownOperatorException;

import java.io.IOException;
import java.text.DecimalFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 异常结果生成监听
 */
@Component
public class AlarmCalculationListener {
    private static final Logger logger = LoggerFactory.getLogger(AlarmCalculationListener.class);
    private ObjectMapper mapper = JsonUtil.getObjectMapper();

    // Json类型转换定义
    private TypeReference measurementTR = new TypeReference<List<Measurement>>(){};
    private Calculator calculator = new Calculator();

    // 异常处理人_系统
    public static final String USER_TITLE_SYSTEM = "System";

    @Autowired
    private MeasRealtimeClient measRealtimeClient;
    @Autowired
    private AlarmEventService alarmEventService;
    @Autowired
    private AbnormalStatusService abnormalStatusService;
    @Autowired
    private AlarmRuleInstanceService alarmRuleInstanceService;
    @Autowired
    private MeasHistoryClient measHistoryClient;
    @Autowired
    private AuthorityClient authorityClient;
    @Autowired
    private DeviceClient deviceClient;
    @Autowired
    private RedisClient redisClient;

    /**
     * todo 完善新增/消除时的点号历史量测逻辑
     * 当点号数量大时，从缓存中获取数据更快
     * @param record
     */
    @KafkaListener(topics = KafkaConsts.Topic.Realtime, groupId = KafkaConsts.Group.AlarmCalculation)
    public void alarmCalculationListener(ConsumerRecord<?, String> record) throws ObjectNotFoundException, IOException {
        String value = record.value();
        if (!StringUtil.isEmpty(value)) {
            List<Measurement> measurements = mapper.readValue(value, measurementTR);
            if (measurements != null && !measurements.isEmpty()) {
                //从缓存中获取告警公式对象 - 数量大时，从MAP中取更快
                Long stationCode = getStationCode(measurements);
                Map<Long, List<AlarmCalculation>> pointInstanceMap = alarmRuleInstanceService.getPointCalculationMapByStationCode(stationCode);
                List<AlarmNotifyDTO> alarmNotifyList = new ArrayList<>();
                Map<String, AlarmEvent> readyEventMap = alarmEventService.getCurrentEventMap();
                for (Measurement measurement : measurements) {
                    Long pointNumber = measurement.getPointNumber();
                    Long rtime = measurement.getRtime();
                    // 告警公式中不包含该点号，或者点号值为空，则跳过
                    if (!pointInstanceMap.containsKey(pointNumber) || measurement.getVal() == null) {
                        continue;
                    }
                    //若告警源点号中包含该点号，则执行相关的新增告警
                    List<AlarmCalculation> ruleInstances = pointInstanceMap.get(pointNumber);
                    if (ruleInstances == null || ruleInstances.isEmpty()) {
                        continue;
                    }
                    //每个公式对象都有满足的阈值区间，按照设计各个阈值区间不会冲突
                    for (AlarmCalculation ruleInstance : ruleInstances) {
                        List<Long> measPointNumbers = ruleInstance.getPoints();
                        //计算包所需参数为Map<Long, Number>，还需要进行转换
                        Map<Long, Double> measPnValue = new HashMap<>();
                        if (measPointNumbers == null || measPointNumbers.isEmpty()) {
                            continue;
                        }
                        List<Long> measPnWithoutValue = new ArrayList<>();
                        for (Long measPointNumber : measPointNumbers) {
                            if (measPointNumber.equals(pointNumber)) {
                                measPnValue.put(measPointNumber, measurement.getVal());
                            } else {
                                measPnWithoutValue.add(measPointNumber);
                            }
                        }
                        if (measPnWithoutValue != null && !measPnWithoutValue.isEmpty()) {
                            Map<Long, Double> preValues = measRealtimeClient.getMeasureRealtime(measPnWithoutValue).getBody().getResults();
                            if (preValues == null || preValues.containsValue(null)) {
                                continue;
                            } else {
                                measPnValue.putAll(preValues);
                            }
                        }
                        String formula = ruleInstance.getFormula();
                        //输出并保存计算公式结果值
                        Double result;
                        try {
                            result = calculator.eval(formula, transferToNumber(measPnValue)).doubleValue();
                        } catch (Exception ex) {
                            logger.error("实时数据新增告警事件报错，{}，{}", record.value(), ex);
                            continue;
                        }
                        //判断结果值是否需要新增异常
                        String key = String.valueOf(ruleInstance.getId());
                        if ((MeasurementConsts.ALARM_STATUS.Alarm.equals(result) && !readyEventMap.containsKey(key))
                                || (MeasurementConsts.ALARM_STATUS.Normal.equals(result) && readyEventMap.containsKey(key))) {
                            alarmNotifyList.add(getAlarmNotify(measPnValue, ruleInstance, rtime, result));
                        }
                    }
                }
                if (alarmNotifyList.size() > 0) {
                    StationDTO station = authorityClient.getStationByCode(stationCode).getBody().getResults();
                    AbnormalStatusDTO ready = abnormalStatusService.findByName(AbnormalConsts.ABNORMAL_STATUS.READY.getName());
                    for (AlarmNotifyDTO alarmNotify : alarmNotifyList) {
                        AlarmCalculation ruleInstance = alarmNotify.getAlarmCalculation();
                        SystemConsts.ACTION action = alarmNotify.getAction();
                        Long rtime = alarmNotify.getTimeStamp();
                        if (action == SystemConsts.ACTION.ADD) {
                            //新增异常事件
                            createEvent(ruleInstance, station, ready, rtime, true);
                        } else if (action == SystemConsts.ACTION.CLEAR) {
                            //消除异常事件
                            String key = String.valueOf(ruleInstance.getId());
                            AlarmEvent alarmEvent = readyEventMap.get(key);
                            if (alarmEvent == null) {
                                continue;
                            }
                            AbnormalStatusDTO eliminated = abnormalStatusService.findByName(AbnormalConsts.ABNORMAL_STATUS.ELIMINATED.getName());
                            String timeZone = authorityClient.getTimeZoneIdByStationId(alarmEvent.getStationId()).getBody().getResults();
                            String dtime = DateTimeUtils.getDateString(rtime, timeZone);
                            alarmEvent.setAbnormalStatusId(eliminated.getId());
                            alarmEvent.setAbnormalStatusName(eliminated.getName());
                            alarmEvent.setAbnormalStatusTitle(eliminated.getTitle());
                            alarmEvent.setLatestProcessTime(dtime);
                            alarmEvent.setLatestProcessRtime(rtime);
                            alarmEvent.setContinueTime(rtime - alarmEvent.getStartRtime());
                            alarmEvent.setUserTitleProcess(USER_TITLE_SYSTEM);
                            try {
                                alarmEventService.updateEvent(alarmEvent);
                            } catch (Exception e) {
                                logger.error("消除告警事件报错,{}", e);
                            }
                        }
                    }
                }
            }
        }
    }

    @KafkaListener(topics = KafkaConsts.Topic.Recollect, groupId = KafkaConsts.Group.AlarmRecalculate)
    public void AlarmRecalculate(ConsumerRecord<?, String> record) {
        try {
            String key = record.value();
            //查找告警处理状态枚举
            AbnormalStatusDTO ready = abnormalStatusService.findByName(AbnormalConsts.ABNORMAL_STATUS.READY.getName());
            AbnormalStatusDTO eliminated = abnormalStatusService.findByName(AbnormalConsts.ABNORMAL_STATUS.ELIMINATED.getName());
            if (ready == null || eliminated == null) {
                logger.warn("未找到待处理或已消除的告警处理状态");
                return;
            }
            if (!StringUtil.isEmpty(key)) {
                //读取推送消息，获取量测列表，电站id，开始和结束时间戳
                DataTransferDTO dataTransferDTO = redisClient.getObject(key, DataTransferDTO.class);
                if (dataTransferDTO == null) {
                    logger.error("通过缓存key获取不到补录数据");
                    return;
                }
                Long stationCode = dataTransferDTO.getStationCode();
                Long startRtime = dataTransferDTO.getStartRtime();
                Long endRtime = dataTransferDTO.getEndRtime();
                List<Measurement> measurementList = dataTransferDTO.getMeasurements();
                StationDTO station = authorityClient.getStationByCode(stationCode).getBody().getResults();
                if (station == null) {
                    logger.error("未找到code为" + stationCode + "的电站");
                    return;
                }
                Long stationId = station.getId();
                //获取电站时区
                String timeZone = authorityClient.getTimeZoneIdByStationId(stationId).getBody().getResults();
                // 组建成点号对应历史量测列表的Map
                Map<Long, List<Measurement>> pidMeasMap = new HashMap<>();
                // 组建成点号+时间戳对应历史量测的Map
                Map<String, Measurement> pidtimeMeasMap = new HashMap<>();
                Set<Long> pidSet = new HashSet<>();
                //遍历历史量测，填充上列两个map
                for (Measurement measurement : measurementList) {
                    Long pid = measurement.getPointNumber();
                    pidSet.add(pid);
                    List<Measurement> value = pidMeasMap.computeIfAbsent(pid, k -> new ArrayList<>());
                    value.add(measurement);
                    pidtimeMeasMap.put(getPidRtimeKey(pid, measurement.getRtime()), measurement);
                }
                // 正式开始处理逻辑
                // 获取历史量测点号相关的告警公式列表并进行处理
                List<Long> pointNumbers = new ArrayList<>(pidSet);
                Map<Long, List<AlarmCalculation>> pointInstanceMap = alarmRuleInstanceService.getPointCalculationMapByStationCode(stationCode);
                for (Long pointNumber : pointNumbers) {
                    if (!pointInstanceMap.containsKey(pointNumber)) {
                        continue;
                    }
                    //若告警源点号中包含该点号，则执行相关的新增告警
                    List<AlarmCalculation> ruleInstances = pointInstanceMap.get(pointNumber);
                    if (ruleInstances == null || ruleInstances.isEmpty()) {
                        continue;
                    }
                    //遍历相关公式 - 伪代码里的公式列表
                    for (AlarmCalculation ruleInstance : ruleInstances) {
                        Long ruleId = ruleInstance.getId();
                        String fomula = ruleInstance.getFormula();
                        List<Long> measPointNumbers = ruleInstance.getPoints();
                        // 获得该公式每个点号的量测时间，从小到大排列，取并集
                        List<Long> rtimeList = getRtimeList(pidMeasMap, measPointNumbers);
                        //新建异常记录列表
                        List<AlarmEvent> alarmEventList = new ArrayList<>();
                        //异常规则和量测点+时间戳-值，便于特殊新增时按照事件开始和结束时间进行量测点取值新增告警量测记录
                        boolean beforeAlarm = false;
                        for (Long rtime : rtimeList) {
                            // 获取到各时间点的量测值，进行告警计算，形成补录告警列表
                            Map<Long, Double> measData = new HashMap<>();
                            for (Long measPointNumber : measPointNumbers) {
                                String pidRtimeKey = getPidRtimeKey(measPointNumber, rtime);
                                Double pointValue;
                                if (pidtimeMeasMap.containsKey(pidRtimeKey)) {
                                    // 如果当前历史告警中存在点号值，则直接拿过来用
                                    pointValue = pidtimeMeasMap.get(pidRtimeKey).getVal();
                                } else {
                                    // 否则，获得在该时间点的历史最新量测值
                                    pointValue = getLastValue(measPointNumber, DateTimeUtils.getDateString(rtime, timeZone));
                                }
                                measData.put(measPointNumber, pointValue);
                            }
                            boolean currentAlarm = doAlarmJudge(fomula, measData);
                            // 如果上次无告警，本次有告警
                            if (!beforeAlarm && currentAlarm) {
                                //新增告警
                                alarmEventList.add(createEvent(ruleInstance, station, ready, rtime, false));
                            } else if (beforeAlarm && !currentAlarm) {
                                //取出上一条记录，设置为消除
                                AlarmEvent alarmEvent = alarmEventList.get(alarmEventList.size() - 1);
                                alarmEvent.setAbnormalStatusId(eliminated.getId());
                                alarmEvent.setLatestProcessTime(DateTimeUtils.getDateString(rtime, timeZone));
                                alarmEvent.setLatestProcessRtime(rtime);
                            } else if (!currentAlarm) {
                                alarmEventList.add(null);
                            }
                            beforeAlarm = currentAlarm;
                        }
                        //获取传参时间前的历史第一个告警
                        AlarmEvent lastOneHistoryEvent = alarmEventService.getLastHistoryEventByStartTime(ruleId, startRtime, endRtime);
                        //仅当未消除时进行处理
                        if (lastOneHistoryEvent != null && !lastOneHistoryEvent.getAbnormalStatusTitle().equals(eliminated.getTitle())) {
                            updateHistoryEvent(lastOneHistoryEvent, alarmEventList, eliminated, rtimeList.get(0), timeZone);
                        }
                        // 如果补录告警列表中，最后一个是告警(不为null)，则作以下判断
                        if (alarmEventList != null && !alarmEventList.isEmpty() && alarmEventList.get(alarmEventList.size() - 1) != null) {
                            // 获得该告警公式补录结束时间后的所有公式相关点号的最小时间A
                            Long nextRtime = Long.MAX_VALUE;
                            for (Long measPointNumber : measPointNumbers) {
                                Measurement measurement = getNextValue(measPointNumber, DateTimeUtils.getDateString(endRtime, timeZone));
                                //小于30S
                                if (measurement != null && (measurement.getRtime() - endRtime) < 30000) {
                                    nextRtime = measurement.getRtime();
                                }
                            }
                            // 判断该时间点是否存在告警
                            if (nextRtime != Long.MAX_VALUE) {
                                Map<Long, Double> nextDataMap = new HashMap<>();
                                for (Long measPointNumber : measPointNumbers) {
                                    Double pointValue = getLastValue(measPointNumber, DateTimeUtils.getDateString(nextRtime, timeZone));
                                    nextDataMap.put(measPointNumber, pointValue);
                                }
                                if (!nextDataMap.values().contains(null)) {
                                    boolean nextAlarm = doAlarmJudge(fomula, nextDataMap);
                                    AlarmEvent lastOne = alarmEventList.get(alarmEventList.size() - 1);
                                    // 如果结果为不告警，则将是否告警列表中最后一个告警状态置为已消除，结束时间置为A
                                    if (!nextAlarm) {
                                        lastOne.setLatestProcessRtime(nextRtime);
                                        lastOne.setLatestProcessTime(DateTimeUtils.getDateString(nextRtime, timeZone));
                                        lastOne.setAbnormalStatusId(eliminated.getId());
                                        // 如果结果为告警，则获取该公式开始时间≥补录结束时间的第一个历史告警
                                    } else {
                                        AlarmEvent nextAlarmHistory = getFirstEventByTimeRange(ruleId, endRtime, Long.MAX_VALUE);
                                        // 第一个历史告警为空，或者历史告警和结束时间下一个量测点的时间相差过大，则按结果为不告警处理
                                        if (nextAlarmHistory == null || nextAlarmHistory.getStartRtime() >= nextRtime) {
                                            lastOne.setLatestProcessRtime(nextRtime);
                                            lastOne.setLatestProcessTime(DateTimeUtils.getDateString(nextRtime, timeZone));
                                            lastOne.setAbnormalStatusId(eliminated.getId());
                                        } else {
                                            // 如果结果为告警，则获取该公式开始时间≥补录结束时间的第一个历史告警
                                            nextAlarmHistory.setStartRtime(lastOne.getStartRtime());
                                            nextAlarmHistory.setStartTime(lastOne.getStartTime());
                                            alarmEventList.remove(alarmEventList.size() - 1);
                                        }
                                    }
                                }
                            }
                        }
                        // 移除空值，并持久化
                        alarmEventList.removeIf(Objects::isNull);
                        //特殊新增接口
                        createHistoryEvent(alarmEventList);
                    }
                }
            }
        } catch (Exception e) {
            logger.error("历史数据新增告警事件报错，{}", record.value());
            e.printStackTrace();
        }
    }

    /**
     * 根据第一个量测判断电站代码
     * 注：为了提升效率，默认同一批量测肯定是同个电站的，如果后面出现多个电站，可以进行分组
     * @param measurements 量测列表
     */
    private Long getStationCode(List<Measurement> measurements) {
        if (measurements != null && !measurements.isEmpty()) {
            Long pointNumber = measurements.get(0).getPointNumber();
            if (pointNumber != null && pointNumber >= 1000L) {
                String pointStr = String.valueOf(pointNumber);
                String prefix = pointStr.substring(0,4);
                Long code = Long.parseLong(prefix);
                if (code == AuthorityConsts.FIRM_CODE.JCDL.value() && pointNumber >= 999900000L) {
                    prefix = pointStr.substring(0,8);
                    code = Long.parseLong(prefix);
                }
                return code;
            }
        }
        return null;
    }

    private AlarmEvent createEvent(AlarmCalculation ruleInstance,
                                   StationDTO station,
                                   AbnormalStatusDTO status,
                                   Long rtime,
                                   boolean needSave) throws ObjectNotFoundException, JsonProcessingException {
        //找出设备和电站ID后新增事件
        DecimalFormat df = new DecimalFormat( "0.00");
        Long stationId = ruleInstance.getStationId();
        String timeZone = authorityClient.getTimeZoneIdByStationId(stationId).getBody().getResults();
        String dtime = DateTimeUtils.getDateString(rtime, timeZone);
        Boolean chinese = SystemConsts.TIME_ZONE.China.equals(timeZone);
        List<Long> relatedPointNumber = ruleInstance.getRelatedPoints();
        AlarmEvent event = new AlarmEvent();
        event.setAlarmId(ruleInstance.getId());
        event.setAlarmTitle(ruleInstance.getTypeTitle());
        event.setStartRtime(rtime);
        event.setStartTime(dtime);
        event.setAlarmLevelId(ruleInstance.getLevelId());
        event.setAlarmLevelName(ruleInstance.getLevelName());
        event.setAlarmLevelTitle(ruleInstance.getLevelTitle());
        event.setAlarmTypeId(ruleInstance.getTypeId());
        event.setAlarmTypeTitle(ruleInstance.getTypeTitle());
        if (station != null) {
            event.setStationId(station.getId());
            event.setStationTitle(station.getTitle());
        }
        if (ruleInstance.getRelDeviceId() != null) {
            event.setDevId(ruleInstance.getRelDeviceId());
            event.setDevTitle(ruleInstance.getRelDeviceTitle());
        } else if (ruleInstance.getRelStationId() != null) {
            event.setDevId(ruleInstance.getRelStationId());
            event.setDevTitle(ruleInstance.getRelStationTitle());
        }
        if (status != null) {
            event.setAbnormalStatusId(status.getId());
            event.setAbnormalStatusName(status.getName());
            event.setAbnormalStatusTitle(status.getTitle());
            if (AbnormalConsts.ABNORMAL_STATUS.ELIMINATED.getName().equals(status.getName())) {
                event.setLatestProcessTime(dtime);
                event.setLatestProcessRtime(rtime);
                event.setUserTitleProcess(USER_TITLE_SYSTEM);
            }
        }
        List<String> recordList = new ArrayList<>();
        recordList.add(ruleInstance.getDesc() == null ? "" : ruleInstance.getDesc());
        if (relatedPointNumber != null && relatedPointNumber.size() != 0) {
            List<Analog> analogList = deviceClient.getAnalogsByPointNumbers(relatedPointNumber).getBody().getResults();
            if (analogList != null && analogList.size() != 0)  {
                Map<Long, Double> analogListValues = redisClient.getMeasurementDouble(relatedPointNumber);
                for (Analog analog : analogList) {
                    Long analogPn = analog.getPointNumber();
                    Double value = analogListValues.get(analogPn);
                    if (value != null) {
                        String record;
                        if (chinese) {
                            record = analog.getTitle() + "为" + df.format(analogListValues.get(analogPn));
                        } else {
                            record = "the value of " + analog.getTitle() + " is " + df.format(analogListValues.get(analogPn));
                        }
                        if (analog.getUnit() != null) {
                            record += analog.getUnit();
                        }
                        recordList.add(record);
                    }
                }
            } else {
                logger.warn("异常规则实例{}未查询到关联点号{}", ruleInstance.getId(), relatedPointNumber);
            }
        }
        event.setRecords(StringUtils.join(recordList.toArray(), ", "));
        if (needSave) {
            try {
                alarmEventService.createEvent(event);
            } catch (Exception e) {
                logger.error("数据补录新增告警事件报错,{}", e);
            }
        }
        return event;
    }

    /**
     * 生成告警通知对象
     * @param pnValueMap 点号值
     * @param ruleInstance 异常实例
     * @param rtime 时间戳
     * @param result 计算结果
     */
    private AlarmNotifyDTO getAlarmNotify(Map<Long, Double> pnValueMap, AlarmCalculation ruleInstance,
                                             Long rtime, Double result) {
        AlarmNotifyDTO alarmNotify = new AlarmNotifyDTO();
        alarmNotify.setPnValueMap(pnValueMap);
        alarmNotify.setAlarmCalculation(ruleInstance);
        alarmNotify.setTimeStamp(rtime);
        if (result == 1) {
            alarmNotify.setAction(SystemConsts.ACTION.ADD);
        } else if (result == 0) {
            alarmNotify.setAction(SystemConsts.ACTION.CLEAR);
        }
        return alarmNotify;
    }

    private Map<String, Number> transferToNumber(Map<Long, Double> map) {
        Map<String, Number> result = new HashMap<>();
        for (Map.Entry entry : map.entrySet()) {
            result.put(String.valueOf(entry.getKey()), (Number) entry.getValue());
        }
        return result;
    }

    /**
     * 生成自定义点号时间戳主键
     * @param pointNumber
     * @param rtime
     * @return
     */
    private String getPidRtimeKey(Long pointNumber, Long rtime) {
        return pointNumber + "_" + rtime;
    }

    /**
     * 根据公式量测点号和点号历史数据获取数据时间戳
     * @param pidMeasMap
     * @param measPn
     * @return
     */
    private List<Long> getRtimeList(Map<Long, List<Measurement>> pidMeasMap, List<Long> measPn) {
        List<Long> rtimeList = new ArrayList<>();
        for (Long pointNumber : measPn) {
            List<Measurement> measurements = pidMeasMap.get(pointNumber);
            if (measurements != null) {
                for (Measurement measurement : measurements) {
                    rtimeList.add(measurement.getRtime());
                }
            }
        }
        rtimeList = rtimeList.stream().distinct().collect(Collectors.toList());
        return rtimeList;
    }

    /**
     * 历史量测 - 获取点号在给定时间之前的最新值
     * @param pointNumber
     * @param dtime
     * @return
     */
    private Double getLastValue(Long pointNumber, String dtime) {
        return measHistoryClient.getPreValue(pointNumber, dtime).getBody().getResults();
    }

    /**
     * 历史量测 - 获取点号在给定时间之后的最新值
     * @param pointNumber
     * @param dtime
     * @return
     */
    private Measurement getNextValue(Long pointNumber, String dtime) {
        return measHistoryClient.getNextMeasurement(pointNumber, dtime).getBody().getResults();
    }

    /**
     * 判断该公式告警值，若公式计算结果值为1则需要告警，否则不需要
     * @param formula
     * @param data
     * @return
     * @throws IncalculableExpressionException
     * @throws IllegalExpressionException
     * @throws UnknownOperatorException
     */
    private boolean doAlarmJudge(String formula, Map<Long, Double> data) throws IncalculableExpressionException, IllegalExpressionException, UnknownOperatorException {
        Double result = calculator.eval(formula, transferToNumber(data)).doubleValue();
        return result == 1;
    }

    /**
     * 根据告警规则ID和时间范围获得范围内的第一条记录
     * @param ruleId
     * @param startRtime
     * @param endRtime
     * @return
     */
    private AlarmEvent getFirstEventByTimeRange(Long ruleId, Long startRtime, Long endRtime) {
        return alarmEventService.getFirstEventByRuleAndTimeRange(ruleId, startRtime, endRtime);
    }

    /**
     * 特殊新增接口
     * @param alarmEventList
     */
    private void createHistoryEvent(List<AlarmEvent> alarmEventList) throws JsonProcessingException, ObjectNotFoundException {
        for (AlarmEvent alarmEvent : alarmEventList) {
            alarmEventService.createForHistory(alarmEvent);
        }
    }

    /**
     * 特殊更新接口，在更新结束时间时，根据量测创建老的量测记录
     * @param alarmEvent
     */
    private void updateHistoryEvent(AlarmEvent alarmEvent) throws ObjectNotFoundException {
        //保存更新过的量测对象
        alarmEventService.updateForHistory(alarmEvent);
    }

    /**
     * type 1为当前历史前，2位当前历史后
     * @param historyEvent
     * @param alarmEventList
     * @param eliminated
     * @param firstRtime
     * @param timeZone
     * @throws ObjectNotFoundException
     */
    private void updateHistoryEvent(AlarmEvent historyEvent,
                                    List<AlarmEvent> alarmEventList,
                                    AbnormalStatusDTO eliminated,
                                    Long firstRtime,
                                    String timeZone) throws ObjectNotFoundException {
        if (alarmEventList !=null && !alarmEventList.isEmpty()) {
            // 如果补录告警列表中，第一个是告警，则将该告警纳入历史告警
            if (alarmEventList.get(0) != null) {
                AlarmEvent firstEvent = alarmEventList.get(0);
                historyEvent.setLatestProcessTime(firstEvent.getLatestProcessTime());
                historyEvent.setLatestProcessRtime(firstEvent.getLatestProcessRtime());
                updateHistoryEvent(historyEvent);
                alarmEventList.remove(0);
            } else {
                // 如果补录告警列表中，第一个不是告警，则将历史告警状态置为已消除，结束时间是补录第一个时间
                historyEvent.setAbnormalStatusId(eliminated.getId());
                historyEvent.setLatestProcessTime(DateTimeUtils.getDateString(firstRtime, timeZone));
                historyEvent.setLatestProcessRtime(firstRtime);
                historyEvent.setAbnormalStatusId(eliminated.getId());
                historyEvent.setAbnormalStatusTitle(eliminated.getTitle());
                updateHistoryEvent(historyEvent);
            }
        }
    }
}
